package pers.cz.postgirl.core.store;

import com.alibaba.fastjson.JSON;
import org.apache.commons.io.input.ReversedLinesFileReader;
import pers.cz.jefconfig.config.JefConfiguration;
import pers.cz.postgirl.config.SystemConfig;
import pers.cz.postgirl.core.Spy;
import pers.cz.postgirl.core.enums.LocalFileEnum;

import pers.cz.postgirl.exception.BusinessException;
import pers.cz.postgirl.exception.ErrorCode;
import pers.cz.postgirl.exception.StoreException;
import pers.cz.postgirl.util.FileUtils;
import pers.cz.postgirl.util.JefLog;
import pers.cz.postgirl.util.SerializeUtils;
import pers.cz.postgirl.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;

/**
 * @program: postgirl-client
 * @description: 本地文件存储
 * 使用文件保存大型数据：
 * 增查：以key value的形式呈现
 * 1、创建索引文件
 *      索引文件记录key对应的value所在的起始位置，并记录value的数据块大小
 * 2、创建数据文件
 *     数据文件只记录value
 * 删改：删除后的空余块如何处理？修改后的块比原来的大，多余的数据块如何处理？
 *
 * @author: Cheng Zhi
 * @create: 2023-08-05 11:41
 **/
public class LocalFileStore implements Store{

    private JefLog jefLog = new JefLog(LocalFileStore.class);
    /**
     * 文件索引名称
     */
    private static final String DATA_INDEX_FILE= "/postGirlDataIndex.index";

    /**
     * 文件名称
     */
    private static final String DATA_FILE = "/postGirlData.data";

    private Set<String> fileNameCache = new HashSet<>();

    public LocalFileStore() {
        init();
    }

    private void init() {
        // 加载存储路径下所有的文件名
        String filePath = JefConfiguration.get(LocalFileEnum.DATA_FILE_PATH, DATA_FILE);
        List<String> fileNames = FileUtils.getFileNames(filePath);
        fileNameCache.addAll(fileNames);
    }

    /**
     * 前置处理
     * @param key xxxx-xxxx-xxxx_md5~1231231231231
     * @return
     */
    private void preSet(String key) {
        String[] s = StringUtils.split(key, "_");
        String caseInfo = s[0];
        for (String fileName : fileNameCache) {
            // 获取caseId
            if (fileName.startsWith(caseInfo)) {
                String[] split = StringUtils.split(fileName, "~");
                long time = Long.valueOf(split[1]);
                if (SystemConfig.getInstance().isTimeOut(time)) {
                    // 如果已经超时，则删除该文件
                    FileUtils.delteFile(getFileName(key));
                }
            }
        }
    }

    /**
     * 获取数据文件
     * @param key
     * @return
     */
    private String getFileName(String key) {
        String filePath = JefConfiguration.get(LocalFileEnum.DATA_FILE_PATH, DATA_FILE);
        String fileName = filePath + "/" + key + ".out";
        return fileName;
    }

    @Override
    public void set(String key, Object value) {

        key = key + "~" + System.currentTimeMillis();
        preSet(key);
        // 将spy对象序列化
        byte[] val = SerializeUtils.serialize(value);
        // 给每个key一个时间范围，比如一分钟，超过一分钟遇到相同的castId并且时间不在范围内，则先删除再新增。
        FileUtils.writeAndOverride(getFileName(key), val);
    }

    @Override
    public Object get(String key) {

        byte[] bytes = getBytes(key);
        Spy spy = SerializeUtils.unserialize(bytes);
        return spy;
    }

    public byte[] getBytes(String key) {

        if (SystemConfig.getInstance().isDebug()) {
            jefLog.debug("==========从文件中获取=============" + key);
        }
        String filePath = JefConfiguration.get(LocalFileEnum.DATA_FILE_PATH, DATA_FILE);

        String fileName = filePath + "/" + key + ".out";
        // 短期内有多个在超时范围内的key数据文件怎么办？已位
        byte[] bytes = FileUtils.readAllBlock(fileName);
        if (bytes != null) {
            if (SystemConfig.getInstance().isDebug()) {
                jefLog.debug("=================获取到了=================" + bytes.length);
            }
        } else {
            jefLog.debug("根据[" + key + "]未获取到结果！！！");
        }

        return bytes;
    }


    private void createDataIndex(String dataKey, long dataSize) {

        String fileName = JefConfiguration.get(LocalFileEnum.DATA_INDEX_FILE_PATH, DATA_INDEX_FILE);
        String dataFileName = JefConfiguration.get(LocalFileEnum.DATA_FILE_PATH, DATA_FILE);
        File dataFile = new File(dataFileName);
        long filePointer = dataFile.length();
        String insertContent = dataKey + "|" + filePointer + "|" + dataSize + "\n";
        FileUtils.writeFileWithStreamByRandomAccess(fileName, insertContent);
    }

    private FileIndexInfo readIndex(String dataKey) {

        // 按行读取
        try {
            ReversedLinesFileReader object = new ReversedLinesFileReader(getIndexFile(), Charset.forName("UTF-8"));
            String line;
            while ((line = object.readLine()) != null) {
                if (line.startsWith(dataKey)) {
                    String[] split = StringUtils.split(line, "|");
                    FileIndexInfo fileIndexInfo = new FileIndexInfo();
                    fileIndexInfo.setStartPoint(Long.valueOf(split[1]));
                    fileIndexInfo.setSize(Integer.valueOf(split[2]));

                    return fileIndexInfo;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    private File getIndexFile() {
        String fileName = JefConfiguration.get(LocalFileEnum.DATA_INDEX_FILE_PATH, DATA_INDEX_FILE);
        return new File(fileName);
    }

   private class FileIndexInfo {

        private long startPoint;
        private int size;

       public long getStartPoint() {
           return startPoint;
       }

       public void setStartPoint(long startPoint) {
           this.startPoint = startPoint;
       }

       public int getSize() {
           return size;
       }

       public void setSize(int size) {
           this.size = size;
       }
   }

}
